/*-------------------------------------------------------------------------
 *
 * pg_wchar.h
 *	  多字节字符支持
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/mb/pg_wchar.h
 *
 *	NOTES
 *		这同时被后端和前端使用，但不应被libpq客户端程序包含。
 *		特别是，libpq客户端不应假设它链接的libpq版本所使用的编码ID与此处声明的ID匹配。
 *
 *-------------------------------------------------------------------------
 */
#ifndef PG_WCHAR_H
#define PG_WCHAR_H

/*
 * pg_wchar 类型
 */
typedef unsigned int pg_wchar;

/*
 * 任何后端编码中多字节字符的最大字节长度
 */
#define MAX_MULTIBYTE_CHAR_LEN	4

/*
 * EUC 的各种定义
 */
#define SS2 0x8e				/* 单字节移位 2 (JIS0201) */
#define SS3 0x8f				/* 单字节移位 3 (JIS0212) */

/*
 * SJIS 验证宏
 */
#define ISSJISHEAD(c) (((c) >= 0x81 && (c) <= 0x9f) || ((c) >= 0xe0 && (c) <= 0xfc))
#define ISSJISTAIL(c) (((c) >= 0x40 && (c) <= 0x7e) || ((c) >= 0x80 && (c) <= 0xfc))

/*----------------------------------------------------
 * MULE 内部编码 (MIC)
 *
 * 此编码遵循 XEmacs 中使用的设计；它旨在
 * 包含许多外部定义的字符集。每个字符包括
 * 所属字符集的标识，因此该编码
 * 是通用的，但略显笨重。
 *
 * 目前 PostgreSQL 支持 5 种 MULE 字符集：
 *
 * 1) 1 字节 ASCII 字符。每个字节都低于 0x80。
 *
 * 2) “官方”单字节字符集，例如 ISO-8859-1 (Latin1)。
 *	  每个 MULE 字符由 2 个字节组成：LC1 + C1，其中 LC1 是
 *	  字符集标识符（范围在 0x81 到 0x8d 之间），C1
 *	  是字符编码（范围在 0xa0 到 0xff 之间）。
 *
 * 3) “私有”单字节字符集，例如 SISHENG。每个 MULE
 *	  字符由 3 个字节组成：LCPRV1 + LC12 + C1，其中 LCPRV1
 *	  是私有字符集标志，LC12 是字符集标识符，
 *	  C1 是字符编码（范围在 0xa0 到 0xff 之间）。
 *	  LCPRV1 要么是 0x9a（如果 LC12 在 0xa0 到 0xdf 之间）
 *	  要么是 0x9b（如果 LC12 在 0xe0 到 0xef 之间）。
 *
 * 4) “官方”多字节字符集，例如 JIS X0208。每个 MULE
 *	  字符由 3 个字节组成：LC2 + C1 + C2，其中 LC2 是
 *	  字符集标识符（范围在 0x90 到 0x99 之间），C1
 *	  和 C2 形成字符编码（每个范围在 0xa0 到 0xff 之间）。
 *
 * 5) “私有”多字节字符集，例如 CNS 11643-1992 Plane 3。
 *	  每个 MULE 字符由 4 个字节组成：LCPRV2 + LC22 + C1 + C2，
 *	  其中 LCPRV2 是私有字符集标志，LC22 是字符集标识符，
 *	  C1 和 C2 形成字符编码（每个范围在
 *	  0xa0 到 0xff 之间）。LCPRV2 要么是 0x9c（如果 LC22 在 0xf0
 *	  到 0xf4 之间）要么是 0x9d（如果 LC22 在 0xf5 到 0xfe 之间）。
 *
 * “官方”编码是指已由
 * XEmacs 项目分配代码号码的编码；“私有”编码具有 PostgreSQL 特定的字符集
 * 标识符。
 *
 * 请参阅 “XEmacs 内部手册”，可在 http://www.xemacs.org
 * 获取更多详细信息。请注意，出于历史原因，Postgres 的
 * 私有字符集标志值与 XEmacs 所说的应该匹配的值不符，
 * 因此这并不完全是 MULE（即使私有字符集也不一定互操作）。
 *
 * 请注意，XEmacs 的实现与 emacs 的实现不同。
 * 我们遵循 emacs 的实现，而不是 XEmacs 的。
 *----------------------------------------------------
 */

/*
 * 字符集标识符（在 MULE 文档中也称为“前导字节”）
 */

/*
 * 官方单字节编码的字符集 ID (0x81-0x8e)
 */
#define LC_ISO8859_1		0x81	/* ISO8859 Latin 1 */
#define LC_ISO8859_2		0x82	/* ISO8859 Latin 2 */
#define LC_ISO8859_3		0x83	/* ISO8859 Latin 3 */
#define LC_ISO8859_4		0x84	/* ISO8859 Latin 4 */
#define LC_TIS620			0x85	/* 泰语（尚不支持） */
#define LC_ISO8859_7		0x86	/* 希腊语（尚不支持） */
#define LC_ISO8859_6		0x87	/* 阿拉伯语（尚不支持） */
#define LC_ISO8859_8		0x88	/* 希伯来语（尚不支持） */
#define LC_JISX0201K		0x89	/* 日语 1 字节假名 */
#define LC_JISX0201R		0x8a	/* 日语 1 字节罗马字 */
/* 请注意，0x8b 在 Emacs 20.7 时似乎未被使用。
 * 但是，以后版本的 Emacs 可能会使用 0x8b。
 */
#define LC_KOI8_R			0x8b	/* 斯拉夫语 KOI8-R */
#define LC_ISO8859_5		0x8c	/* ISO8859 斯拉夫语 */
#define LC_ISO8859_9		0x8d	/* ISO8859 Latin 5（尚不支持） */
#define LC_ISO8859_15		0x8e	/* ISO8859 Latin 15（尚不支持） */
/* #define CONTROL_1		0x8f 控制字符（未使用） */

/* 是“官方”单字节编码的前导字节？ */
#define IS_LC1(c)	((unsigned char)(c) >= 0x81 && (unsigned char)(c) <= 0x8d)

/*
 * 官方多字节编码的字符集 ID (0x90-0x99)
 * 0x9a-0x9d 是自由的。0x9e 和 0x9f 是保留的。
 */
#define LC_JISX0208_1978	0x90	/* 日语汉字，旧 JIS（不支持） */
#define LC_GB2312_80		0x91	/* Chinese */
#define LC_JISX0208			0x92	/* 日语汉字（JIS X 0208） */
#define LC_KS5601			0x93	/* Korean */
#define LC_JISX0212			0x94	/* 日语汉字（JIS X 0212） */
#define LC_CNS11643_1		0x95	/* CNS 11643-1992 平面 1 */
#define LC_CNS11643_2		0x96	/* CNS 11643-1992 平面 2 */
#define LC_JISX0213_1		0x97	/* 日本汉字 (JIS X 0213 平面 1)
									 * (不支持) */
#define LC_BIG5_1			0x98	/* 平面 1 繁体中文 (不支持) */
#define LC_BIG5_2			0x99	/* 平面 1 繁体中文 (不支持) */

/* 是否是“官方”多字节编码的前导字节？ */
#define IS_LC2(c)	((unsigned char)(c) >= 0x90 && (unsigned char)(c) <= 0x99)

/*
 * Postgres专用前缀字节用于“私有”单字节编码
 * （根据MULE文档，我们应该使用0x9e）
 */
#define LCPRV1_A		0x9a
#define LCPRV1_B		0x9b
#define IS_LCPRV1(c)	((unsigned char)(c) == LCPRV1_A || (unsigned char)(c) == LCPRV1_B)
#define IS_LCPRV1_A_RANGE(c)	\
	((unsigned char)(c) >= 0xa0 && (unsigned char)(c) <= 0xdf)
#define IS_LCPRV1_B_RANGE(c)	\
	((unsigned char)(c) >= 0xe0 && (unsigned char)(c) <= 0xef)

/*
 * Postgres专用前缀字节用于“私有”多字节编码
 * （根据MULE文档，我们应该使用0x9f）
 */
#define LCPRV2_A		0x9c
#define LCPRV2_B		0x9d
#define IS_LCPRV2(c)	((unsigned char)(c) == LCPRV2_A || (unsigned char)(c) == LCPRV2_B)
#define IS_LCPRV2_A_RANGE(c)	\
	((unsigned char)(c) >= 0xf0 && (unsigned char)(c) <= 0xf4)
#define IS_LCPRV2_B_RANGE(c)	\
	((unsigned char)(c) >= 0xf5 && (unsigned char)(c) <= 0xfe)

/*
 * 私有单字节编码的字符集ID (0xa0-0xef)
 */
#define LC_SISHENG			0xa0	/* 中文四声字母用于
									 * 拼音/注音 (不支持) */
#define LC_IPA				0xa1	/* IPA（国际音标协会） (不支持) */
#define LC_VISCII_LOWER		0xa2	/* 越南语 VISCII 1.1 小写 (不支持) */
#define LC_VISCII_UPPER		0xa3	/* 越南语 VISCII 1.1 大写 (不支持) */
#define LC_ARABIC_DIGIT		0xa4	/* 阿拉伯数字 (不支持) */
#define LC_ARABIC_1_COLUMN	0xa5	/* 阿拉伯 1 列 (不支持) */
#define LC_ASCII_RIGHT_TO_LEFT	0xa6	/* ASCII（ISO8859-1 的左半部分）与
										 * 从右到左的方向 (不支持) */
#define LC_LAO				0xa7	/* 老挝字母 (ISO10646 0E80..0EDF)
									 * (不支持) */
#define LC_ARABIC_2_COLUMN	0xa8	/* 阿拉伯 1 列 (不支持) */

/*
 * 私有多字节编码的字符集ID (0xf0-0xff)
 */
#define LC_INDIAN_1_COLUMN	0xf0	/* 印度字符集用于 1 列宽
									 * 字形 (不支持) */
#define LC_TIBETAN_1_COLUMN 0xf1	/* 藏文 1 列宽字形 (不支持) */
#define LC_UNICODE_SUBSET_2 0xf2	/* 范围为 U+2500..U+33FF 的 Unicode 字符 (不支持) */
#define LC_UNICODE_SUBSET_3 0xf3	/* 范围为 U+E000..U+FFFF 的 Unicode 字符 (不支持) */
#define LC_UNICODE_SUBSET	0xf4	/* 范围为 U+0100..U+24FF 的 Unicode 字符 (不支持) */
#define LC_ETHIOPIC			0xf5	/* 以提字母 (不支持) */
#define LC_CNS11643_3		0xf6	/* CNS 11643-1992 平面 3 */
#define LC_CNS11643_4		0xf7	/* CNS 11643-1992 平面 4 */
#define LC_CNS11643_5		0xf8	/* CNS 11643-1992 平面 5 */
#define LC_CNS11643_6		0xf9	/* CNS 11643-1992 平面 6 */
#define LC_CNS11643_7		0xfa	/* CNS 11643-1992 平面 7 */
#define LC_INDIAN_2_COLUMN	0xfb	/* 印度字符集用于 2 列宽
									 * 字形 (不支持) */
#define LC_TIBETAN			0xfc	/* 藏文 (不支持) */
/* #define FREE				0xfd	空闲 (未使用) */
/* #define FREE				0xfe	空闲 (未使用) */
/* #define FREE				0xff	空闲 (未使用) */

/*----------------------------------------------------
 * MULE 相关内容结束
 *----------------------------------------------------
 */

/*
 * PostgreSQL 编码标识符
 *
 * 警告：此枚举的顺序必须与 pg_enc2name_tbl[] 数组中
 *			的条目顺序相同（在 src/common/encnames.c 中），并且
 *			在 pg_wchar_table[] 数组中（在 src/common/wchar.c 中）!
 *
 *			如果你添加了一些编码，请不要忘记检查
 *			PG_ENCODING_BE_LAST 宏。
 *
 * PG_SQL_ASCII 是默认编码，必须为 = 0。
 *
 * XXX 我们必须避免重新编号任何后端编码，直到 libpq 的主要
 * 版本号超过 5；事实证明，后端
 * 编码 ID 实际上是 libpq 的 ABI 的一部分，就 8.2 initdb 和
 * psql 而言。
 */
typedef enum pg_enc
{
	PG_SQL_ASCII = 0,			/* SQL/ASCII */
	PG_EUC_JP,					/* 日本语的 EUC 编码 */
	PG_EUC_CN,					/* 中文的 EUC 编码 */
	PG_EUC_KR,					/* 韩语的 EUC 编码 */
	PG_EUC_TW,					/* 台湾的 EUC 编码 */
	PG_EUC_JIS_2004,			/* EUC-JIS-2004 */
	PG_UTF8,					/* Unicode UTF8 */
	PG_MULE_INTERNAL,			/* Mule 内部代码 */
	PG_LATIN1,					/* ISO-8859-1 拉丁1 */
	PG_LATIN2,					/* ISO-8859-2 拉丁语 2 */
	PG_LATIN3,					/* ISO-8859-3 拉丁语 3 */
	PG_LATIN4,					/* ISO-8859-4 拉丁语 4 */
	PG_LATIN5,					/* ISO-8859-9 拉丁语 5 */
	PG_LATIN6,					/* ISO-8859-10 拉丁6 */
	PG_LATIN7,					/* ISO-8859-13 拉丁7 */
	PG_LATIN8,					/* ISO-8859-14 拉丁8 */
	PG_LATIN9,					/* ISO-8859-15 拉丁9 */
	PG_LATIN10,					/* ISO-8859-16 拉丁10 */
	PG_WIN1256,					/* windows-1256 */
	PG_WIN1258,					/* Windows-1258 */
	PG_WIN866,					/* (MS-DOS CP866) */
	PG_WIN874,					/* windows-874 */
	PG_KOI8R,					/* KOI8-R */
	PG_WIN1251,					/* windows-1251 */
	PG_WIN1252,					/* windows-1252 */
	PG_ISO_8859_5,				/* ISO-8859-5 */
	PG_ISO_8859_6,				/* ISO-8859-6 */
	PG_ISO_8859_7,				/* ISO-8859-7 */
	PG_ISO_8859_8,				/* ISO-8859-8 */
	PG_WIN1250,					/* windows-1250 */
	PG_WIN1253,					/* windows-1253 */
	PG_WIN1254,					/* windows-1254 */
	PG_WIN1255,					/* windows-1255 */
	PG_WIN1257,					/* windows-1257 */
	PG_KOI8U,					/* KOI8-U */
	/* PG_ENCODING_BE_LAST 指向上述条目 */

	/* 以下内容仅用于客户端编码 */
	PG_SJIS,					/* Shift JIS (Windows-932) */
	PG_BIG5,					/* Big5 (Windows-950) */
	PG_GBK,						/* GBK (Windows-936) */
	PG_UHC,						/* UHC (Windows-949) */
	PG_GB18030,					/* GB18030 */
	PG_JOHAB,					/* 韩语 JOHAB 的 EUC */
	PG_SHIFT_JIS_2004,			/* Shift-JIS-2004 */
	_PG_LAST_ENCODING_			/* 仅标记 */

} pg_enc;

#define PG_ENCODING_BE_LAST PG_KOI8U

/*
 * 请在访问 pg_enc2name_tbl[] 或其他地方之前使用这些测试...
 */
#ifdef FDD
#define PG_VALID_BE_ENCODING(_enc) (((_enc) >= 0 && (_enc) <= PG_ENCODING_BE_LAST) || (_enc) == PG_GB18030)
#else
#define PG_VALID_BE_ENCODING(_enc) \
		((_enc) >= 0 && (_enc) <= PG_ENCODING_BE_LAST)
#endif //FDD

#define PG_ENCODING_IS_CLIENT_ONLY(_enc) \
		((_enc) > PG_ENCODING_BE_LAST && (_enc) < _PG_LAST_ENCODING_)

#define PG_VALID_ENCODING(_enc) \
		((_enc) >= 0 && (_enc) < _PG_LAST_ENCODING_)

/* 在 FE 上可以使用所有编码 */
#define PG_VALID_FE_ENCODING(_enc)	PG_VALID_ENCODING(_enc)

/*
 * 在不同编码之间转换字符串时，我们假设转换结果的空间
 * 在最坏的情况下是 4 倍的增长。当前支持的编码对的
 * 速率在 3 以内（SJIS JIS X0201 半宽
 * 假名 -> UTF8 是最坏的情况）。所以目前 "4" 应该足够。
 *
 * 请注意，这与任何特定编码中的最大字符宽度是不同的。
 */
#define MAX_CONVERSION_GROWTH  4

/*
 * 在任何编码中转换至少一个字符到任何其他编码所需的字符串
 * 最大字节长度。换句话说，如果你将 MAX_CONVERSION_INPUT_LENGTH 字节
 * 提供给任何编码转换函数，保证能够在不需要更多输入的情况下
 * 转换某些内容（假设输入是有效的）。
 *
 * 当前，最坏的情况是 UTF8 -> SJIS JIS X0201 半宽
 * 假名的转换，其中一对 UTF-8 字符被转换为一个
 * SHIFT_JIS_2004 字符（这是 MAX_CONVERSION_GROWTH 最坏情况的逆）。它需要 6 字节输入。
 * 理论上，用户定义的转换函数可能有更复杂的情况，尽管
 * 对于反向映射，您也可能需要提高 MAX_CONVERSION_GROWTH。
 * 但在这里不需要吝啬，所以请让它慷慨一点。
 */
#define MAX_CONVERSION_INPUT_LENGTH	16

/*
 * 与任何一个 Unicode 代码点相等的字符串的最大字节长度，
 * 在任何后端编码中。当前值假设 4 字节 UTF-8
 * 字符可能因 MAX_CONVERSION_GROWTH 而扩展，这是一个巨大的
 * 高估。但在目前的使用中，我们不分配该值的大倍数，
 * 因此在这里小气没有太大意义。
 */
#define MAX_UNICODE_EQUIVALENT_STRING	16

/*
 * 用于将编码编号映射到官方编码名称和
 * 可能其他附属数据的表。访问表条目之前请小心检查编码编号！
 *
 * if (PG_VALID_ENCODING(encoding))
 *		pg_enc2name_tbl[ encoding ];
 */
typedef struct pg_enc2name
{
	const char *name;
	pg_enc		encoding;
#ifdef WIN32
	unsigned	codepage;		/* WIN32 的编码页 */
#endif
} pg_enc2name;

extern PGDLLIMPORT const pg_enc2name pg_enc2name_tbl[];

/*
 * gettext 的编码名称
 */
typedef struct pg_enc2gettext
{
	pg_enc		encoding;
	const char *name;
} pg_enc2gettext;

extern PGDLLIMPORT const pg_enc2gettext pg_enc2gettext_tbl[];

/*
 * pg_wchar 内容
 */
typedef int (*mb2wchar_with_len_converter) (const unsigned char *from,
											pg_wchar *to,
											int len);

typedef int (*wchar2mb_with_len_converter) (const pg_wchar *from,
											unsigned char *to,
											int len);

typedef int (*mblen_converter) (const unsigned char *mbstr);

typedef int (*mbdisplaylen_converter) (const unsigned char *mbstr);

typedef bool (*mbcharacter_incrementer) (unsigned char *mbstr, int len);

typedef int (*mbchar_verifier) (const unsigned char *mbstr, int len);

typedef int (*mbstr_verifier) (const unsigned char *mbstr, int len);

typedef struct
{
	mb2wchar_with_len_converter mb2wchar_with_len;	/* 将多字节字符串转换为 wchar */
	wchar2mb_with_len_converter wchar2mb_with_len;	/* 将 wchar 字符串转换为多字节 */
	mblen_converter mblen;		/* 获取字符的字节长度 */
	mbdisplaylen_converter dsplen;	/* 获取字符的显示宽度 */
	mbchar_verifier mbverifychar;	/* 验证多字节字符 */
	mbstr_verifier mbverifystr; /* 验证多字节字符串 */
	int			maxmblen;		/* 在此编码中字符的最大字节数 */
} pg_wchar_tbl;

extern PGDLLIMPORT const pg_wchar_tbl pg_wchar_table[];

/*
 * UTF-8 和其他编码之间转换的数据结构
 * (UtfToLocal() 和 LocalToUtf())。在这些数据结构中，任一编码的字符
 * 由 uint32 字表示；因此我们只能支持最大 4 字节长的字符。
 * 例如，字节序列 0xC2 0x89 将由 0x0000C289 表示，
 * 0xE8 0xA2 0xB4 将由 0x00E8A2B4 表示。
 *
 * 有三种可能的方式映射字符：
 *
 * 1. 使用基数树，从源代码到目标代码。
 * 2. 使用已排序的源 -> 目标代码对数组。此
 *	  方法用于“组合”字符。它们非常少，以至于构建基数树
 *	  是浪费的。
 * 3. 使用转换函数。
 */

/*
 * 字符转换的基数树。
 *
 * 从逻辑上讲，这实际上是四种不同的基数树，适用于 1 字节，
 * 2 字节，3 字节和 4 字节输入。1 字节树是从源到目标代码的简单查找
 * 表。2 字节树由两级组成：
 * 一张查找表用于第一个字节，其中查找表中的值指向第二个字节的查找表。
 * 依此类推。
 *
 * 从物理上讲，所有树都存储在一个大的数组中，根据需要表示的最大值
 * 存储在 'chars16' 或 'chars32' 中。对于每棵树中的每个级别，
 * 我们还存储允许值的下限和上限 - 超出这些范围的值被视为无效，且被排除在外。
 * 
 * 在树的中间级别，存储的值是对 chars[16|32] 数组的偏移量。
 *
 * 在 chars[16|32] 数组的开头，总是有若干个零，
 * 以便您可以安全地按照中间表的索引执行操作而无需显式检查零。
 * 跟随零任何次数总会将您带到开头的虚拟全零表。这有助于在查找值时减少一些周期。
 */
typedef struct
{
	/*
	 * 包含所有值的数组。仅使用chars16或chars32中的一个，
	 * 具体取决于我们需要表示的值的宽度。
	 */
	const uint16 *chars16;
	const uint32 *chars32;

	/* 1字节输入的基数树 */
	uint32		b1root;			/* chars[16|32]数组中表的偏移量 */
	uint8		b1_lower;		/* 单字节输入的最小允许值 */
	uint8		b1_upper;		/* 单字节输入的最大允许值 */

	/* 基数树用于2字节输入 */
	uint32		b2root;			/* 第一个字节表的偏移量 */
	uint8		b2_1_lower;		/* 第一个输入字节的最小/最大允许值 */
	uint8		b2_1_upper;
	uint8		b2_2_lower;		/* 第二个输入字节的最小/最大允许值 */
	uint8		b2_2_upper;

	/* 基数树用于3字节输入 */
	uint32		b3root;			/* 第一个字节表的偏移量 */
	uint8		b3_1_lower;		/* 第一个输入字节的最小/最大允许值 */
	uint8		b3_1_upper;
	uint8		b3_2_lower;		/* 第二个输入字节的最小/最大允许值 */
	uint8		b3_2_upper;
	uint8		b3_3_lower;		/* 第三个输入字节的最小/最大允许值 */
	uint8		b3_3_upper;

	/* 基数树用于4字节输入 */
	uint32		b4root;			/* 第一个字节表的偏移量 */
	uint8		b4_1_lower;		/* 第一个输入字节的最小/最大允许值 */
	uint8		b4_1_upper;
	uint8		b4_2_lower;		/* 第二个输入字节的最小/最大允许值 */
	uint8		b4_2_upper;
	uint8		b4_3_lower;		/* 第三个输入字节的最小/最大允许值 */
	uint8		b4_3_upper;
	uint8		b4_4_lower;		/* 第四个输入字节的最小/最大允许值 */
	uint8		b4_4_upper;

} pg_mb_radix_tree;

/*
 * UTF-8到本地编码转换映射（用于组合字符）
 */
typedef struct
{
	uint32		utf1;			/* UTF-8编码1 */
	uint32		utf2;			/* UTF-8编码2 */
	uint32		code;			/* 本地编码 */
} pg_utf_to_local_combined;

/*
 * 本地编码到UTF-8的转换映射（用于组合字符）
 */
typedef struct
{
	uint32		code;			/* 本地编码 */
	uint32		utf1;			/* UTF-8编码1 */
	uint32		utf2;			/* UTF-8编码2 */
} pg_local_to_utf_combined;

/*
 * 算法编码转换的回调函数（任一方向）
 *
 * 如果函数返回零，它不知道如何转换代码
 */
typedef uint32 (*utf_local_conversion_func) (uint32 code);

/*
 * 支持宏用于编码转换函数以验证其
 * 参数。（如果我们在这里包含fmgr.h，这可以变得更紧凑，
 * 但我们不想这样做，因为此头文件也
 * 被前端使用。）
 */
#define CHECK_ENCODING_CONVERSION_ARGS(srcencoding,destencoding) \
	check_encoding_conversion_args(PG_GETARG_INT32(0), \
								   PG_GETARG_INT32(1), \
								   PG_GETARG_INT32(4), \
								   (srcencoding), \
								   (destencoding))


/*
 * 一些用于Unicode特定测试的便利函数。
 */
static inline bool
is_valid_unicode_codepoint(pg_wchar c)
{
	return (c > 0 && c <= 0x10FFFF);
}

static inline bool
is_utf16_surrogate_first(pg_wchar c)
{
	return (c >= 0xD800 && c <= 0xDBFF);
}

static inline bool
is_utf16_surrogate_second(pg_wchar c)
{
	return (c >= 0xDC00 && c <= 0xDFFF);
}

static inline pg_wchar
surrogate_pair_to_codepoint(pg_wchar first, pg_wchar second)
{
	return ((first & 0x3FF) << 10) + 0x10000 + (second & 0x3FF);
}


/*
 * 这些函数被视为libpq导出的API的一部分，并且
 * 也在libpq-fe.h中声明。
 */
extern int	pg_char_to_encoding(const char *name);
extern const char *pg_encoding_to_char(int encoding);
extern int	pg_valid_server_encoding_id(int encoding);

/*
 * 这些函数可供与libpgcommon链接的前端代码使用
 * （除了刚才提到的那些）。此文件中早些声明的
 * 常量表也可从libpgcommon中获得。
 */
extern void pg_encoding_set_invalid(int encoding, char *dst);
extern int	pg_encoding_mblen(int encoding, const char *mbstr);
extern int	pg_encoding_mblen_bounded(int encoding, const char *mbstr);
extern int	pg_encoding_dsplen(int encoding, const char *mbstr);
extern int	pg_encoding_verifymbchar(int encoding, const char *mbstr, int len);
extern int	pg_encoding_verifymbstr(int encoding, const char *mbstr, int len);
extern int	pg_encoding_max_length(int encoding);
extern int	pg_valid_client_encoding(const char *name);
extern int	pg_valid_server_encoding(const char *name);
extern bool is_encoding_supported_by_icu(int encoding);
extern const char *get_encoding_name_for_icu(int encoding);

extern unsigned char *unicode_to_utf8(pg_wchar c, unsigned char *utf8string);
extern pg_wchar utf8_to_unicode(const unsigned char *c);
extern bool pg_utf8_islegal(const unsigned char *source, int length);
extern int	pg_utf_mblen(const unsigned char *s);
extern int	pg_mule_mblen(const unsigned char *s);

/*
 * 其余函数仅限于后端使用。
 */
extern int	pg_mb2wchar(const char *from, pg_wchar *to);
extern int	pg_mb2wchar_with_len(const char *from, pg_wchar *to, int len);
extern int	pg_encoding_mb2wchar_with_len(int encoding,
										  const char *from, pg_wchar *to, int len);
extern int	pg_wchar2mb(const pg_wchar *from, char *to);
extern int	pg_wchar2mb_with_len(const pg_wchar *from, char *to, int len);
extern int	pg_encoding_wchar2mb_with_len(int encoding,
										  const pg_wchar *from, char *to, int len);
extern int	pg_char_and_wchar_strcmp(const char *s1, const pg_wchar *s2);
extern int	pg_wchar_strncmp(const pg_wchar *s1, const pg_wchar *s2, size_t n);
extern int	pg_char_and_wchar_strncmp(const char *s1, const pg_wchar *s2, size_t n);
extern size_t pg_wchar_strlen(const pg_wchar *wstr);
extern int	pg_mblen(const char *mbstr);
extern int	pg_dsplen(const char *mbstr);
extern int	pg_mbstrlen(const char *mbstr);
extern int	pg_mbstrlen_with_len(const char *mbstr, int len);
extern int	pg_mbcliplen(const char *mbstr, int len, int limit);
extern int	pg_encoding_mbcliplen(int encoding, const char *mbstr,
								  int len, int limit);
extern int	pg_mbcharcliplen(const char *mbstr, int len, int limit);
extern int	pg_database_encoding_max_length(void);
extern mbcharacter_incrementer pg_database_encoding_character_incrementer(void);

extern int	PrepareClientEncoding(int encoding);
extern int	SetClientEncoding(int encoding);
extern void InitializeClientEncoding(void);
extern int	pg_get_client_encoding(void);
extern const char *pg_get_client_encoding_name(void);

extern void SetDatabaseEncoding(int encoding);
extern int	GetDatabaseEncoding(void);
extern const char *GetDatabaseEncodingName(void);
extern void SetMessageEncoding(int encoding);
extern int	GetMessageEncoding(void);

#ifdef ENABLE_NLS
extern int	pg_bind_textdomain_codeset(const char *domainname);
#endif

extern unsigned char *pg_do_encoding_conversion(unsigned char *src, int len,
												int src_encoding,
												int dest_encoding);
extern int	pg_do_encoding_conversion_buf(Oid proc,
										  int src_encoding,
										  int dest_encoding,
										  unsigned char *src, int srclen,
										  unsigned char *dst, int dstlen,
										  bool noError);

extern char *pg_client_to_server(const char *s, int len);
extern char *pg_server_to_client(const char *s, int len);
extern char *pg_any_to_server(const char *s, int len, int encoding);
extern char *pg_server_to_any(const char *s, int len, int encoding);

extern void pg_unicode_to_server(pg_wchar c, unsigned char *s);

extern unsigned short BIG5toCNS(unsigned short big5, unsigned char *lc);
extern unsigned short CNStoBIG5(unsigned short cns, unsigned char lc);

extern int	UtfToLocal(const unsigned char *utf, int len,
					   unsigned char *iso,
					   const pg_mb_radix_tree *map,
					   const pg_utf_to_local_combined *cmap, int cmapsize,
					   utf_local_conversion_func conv_func,
					   int encoding, bool noError);
extern int	LocalToUtf(const unsigned char *iso, int len,
					   unsigned char *utf,
					   const pg_mb_radix_tree *map,
					   const pg_local_to_utf_combined *cmap, int cmapsize,
					   utf_local_conversion_func conv_func,
					   int encoding, bool noError);

extern bool pg_verifymbstr(const char *mbstr, int len, bool noError);
extern bool pg_verify_mbstr(int encoding, const char *mbstr, int len,
							bool noError);
extern int	pg_verify_mbstr_len(int encoding, const char *mbstr, int len,
								bool noError);

extern void check_encoding_conversion_args(int src_encoding,
										   int dest_encoding,
										   int len,
										   int expected_src_encoding,
										   int expected_dest_encoding);

extern void report_invalid_encoding(int encoding, const char *mbstr, int len) pg_attribute_noreturn();
extern void report_untranslatable_char(int src_encoding, int dest_encoding,
									   const char *mbstr, int len) pg_attribute_noreturn();

extern int	local2local(const unsigned char *l, unsigned char *p, int len,
						int src_encoding, int dest_encoding,
						const unsigned char *tab, bool noError);
extern int	latin2mic(const unsigned char *l, unsigned char *p, int len,
					  int lc, int encoding, bool noError);
extern int	mic2latin(const unsigned char *mic, unsigned char *p, int len,
					  int lc, int encoding, bool noError);
extern int	latin2mic_with_table(const unsigned char *l, unsigned char *p,
								 int len, int lc, int encoding,
								 const unsigned char *tab, bool noError);
extern int	mic2latin_with_table(const unsigned char *mic, unsigned char *p,
								 int len, int lc, int encoding,
								 const unsigned char *tab, bool noError);

#ifdef WIN32
extern WCHAR *pgwin32_message_to_UTF16(const char *str, int len, int *utf16len);
#endif

#endif							/* PG_WCHAR_H */
